package com.amtf.datafile3.core.xsv;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.ArrayList;
import java.util.List;

import com.amtf.datafile3.DataException;
import com.amtf.datafile3.DataRecord;
import com.amtf.datafile3.DataTable;
import com.amtf.datafile3.DataTableDefinition;
import com.amtf.datafile3.core.std.AbsDataTable;

public class XsvDataTable extends AbsDataTable<String, String> {

	protected static final String CRLF = "\r\n";

//	private LineReader bufferedReader = null;
	private BufferedReader bufferedReader = null;
	
	private InputStreamReader streamReader = null;
	private InputStream inStream = null;

	private OutputStreamWriter streamWriter = null;
	private OutputStream outStream = null;

	private File file = null;
	private String charset = "UTF-8";
	// private char delimiter;
	// private char smartQuote;
	private int startRow = 1;
	private boolean firstRowTitle;

	public XsvDataTable(String name, DataTableDefinition definition, File file, String charset, int startRow, boolean firstRowTitle)
	// public XsvDataTable(String name, DataTableDefinition definition, File file, String charset, char delimiter, char smartQuote, int startRow, boolean firstRowTitle)
			throws DataException {
		super(name, definition);
		this.file = file;
		this.charset = charset;
		// this.delimiter = delimiter;
		// this.smartQuote = smartQuote;
		this.startRow = startRow;
		this.firstRowTitle = firstRowTitle;

		createWriter(true);

		createReader();

		readTitleLine();

		detectFieldsDefinition();
	}

	public DataRecord readFirstLine() throws DataException {
		createReader();

		readTitleLine();

		return readNextLine();
	}

	public DataRecord readNextLine() throws DataException {
		String row = null;
		try {
			row = bufferedReader.readLine();
		} catch (IOException e) {
			throw new DataException(e);
		}

		if (row != null) {
			currentReadingLine++;
			return recordParser.parse(definition, row);
		}

		return null;
	}

	public List<DataRecord> readFirstLines(int readCount) throws DataException {
		createReader();

		readTitleLine();

		return readNextLines(readCount);

		// DataRecord record = detectNextFields();
		//
		// int index = 1;
		// List<DataRecord> content = new ArrayList<DataRecord>(readCount);
		// String row = null;
		// while (record != null) {
		// content.add(record);
		//
		// if (index >= readCount) {
		// return content;
		// }
		//
		// try {
		// row = bufferedReader.readLine();
		// } catch (IOException e) {
		// throw new DataException(e);
		// }
		//
		// currentReadingLine++;
		// index++;
		//
		// record = recordParser.parse(definition, row);
		// }

		// return content.size() == 0 ? null : content;
	}

	public List<DataRecord> readNextLines(int readCount) throws DataException {
		if (readCount <= 0) {
			return null;
		}

		int index = 1;
		List<DataRecord> content = new ArrayList<DataRecord>(readCount);

		do {
			String row = null;
			try {
				row = bufferedReader.readLine();
			} catch (IOException e) {
				throw new DataException(e);
			}
			if (row == null) {
				if (content.size() == 0) {
					content = null;
				}
				break;
			} else {
				content.add(recordParser.parse(definition, row));
				currentReadingLine++;
				index++;
			}
		} while (index <= readCount);

		return content;
	}

	public void writeRecord(DataRecord row) throws DataException {
		try {
			streamWriter.write(recordParser.parse(definition, row) + CRLF);
			// streamWriter.write(recordRender.rending(row) + CRLF);
		} catch (IOException e) {
			throw new DataException(e);
		}
		currentWritenLine++;
	}

	public void writeRecord(List<DataRecord> rows) throws DataException {
		if (rows != null) {
			for (DataRecord row : rows) {
				try {
					streamWriter.write(recordParser.parse(definition, row) + CRLF);
					// streamWriter.write(recordRender.rending(row) + CRLF);
				} catch (IOException e) {
					throw new DataException(e);
				}
				currentWritenLine++;
			}
		}
	}

	public void writeTitle(String[] title) throws DataException {
		try {
			streamWriter.write(recordRender.rendingTitle(title) + CRLF);
		} catch (IOException e) {
			throw new DataException(e);
		}
		currentWritenLine++;
	}

	// public void writeRecord(String[] line) throws DataException {
	// try {
	// streamWriter.write(recordParser.parse(definition, line) + CRLF);
	// } catch (IOException e) {
	// throw new DataException(e);
	// }
	// currentWritenLine++;
	// }
	//
	// public void writeRecord(Object[] line) throws DataException {
	// try {
	// streamWriter.write(recordParser.parse(definition, line) + CRLF);
	// } catch (IOException e) {
	// throw new DataException(e);
	// }
	// currentWritenLine++;
	// }

	public void writeAll(DataTable tableSet) throws DataException {

		List<DataRecord> lines = tableSet.readFirstLines(500);
		if (lines != null && lines.size() > 0) {
			if (firstRowTitle) {
				writeTitle(definition.getFieldNames());
			}

			do {
				this.writeRecord(lines);
				lines = tableSet.readNextLines(500);
			} while (lines != null && lines.size() > 0);
		}
	}

	public void close() {
		if (bufferedReader != null) {
			try {
				bufferedReader.close();
			} catch (IOException e) {
			}
			bufferedReader = null;
		}

		if (streamReader != null) {
			try {
				streamReader.close();
			} catch (IOException e) {
			}
			streamReader = null;
		}

		if (inStream != null) {
			try {
				inStream.close();
			} catch (IOException e) {
			}
			inStream = null;
		}

		if (streamWriter != null) {
			try {
				streamWriter.flush();
				streamWriter.close();
			} catch (IOException e) {
			}
			streamWriter = null;
		}

		if (outStream != null) {
			try {
				outStream.close();
			} catch (IOException e) {
			}
			outStream = null;
		}
	}

	// ----------------------------------------------------------------------------------------------

	private void readTitleLine() throws DataException {
		if (firstRowTitle) {
			String title = null;
			try {
				title = bufferedReader.readLine();
			} catch (IOException e) {
				throw new DataException(e);
			}

			if (title != null) {
				currentReadingLine++;
				recordParser.detectColumn(definition, title, firstRowTitle);
			}
		}
	}

	private void detectFieldsDefinition() throws DataException {
		String row = null;
		try {
			row = bufferedReader.readLine();
		} catch (IOException e) {
			throw new DataException(e);
		}

		if (row != null) {
			recordParser.detectColumn(definition, row, false);
		}
	}

	// private void resetFieldsType() throws DataException {
	// String row = null;
	// try {
	// row = bufferedReader.readLine();
	// } catch (IOException e) {
	// throw new DataException(e);
	// }
	//
	// currentReadingLine++;
	//
	// recordParser.detectFieldsType(definition, row);
	// }

	private void createReader() throws DataException {
		try {
			if (this.bufferedReader != null) {
				this.bufferedReader.close();
				this.streamReader.close();
				this.inStream.close();
			}
			this.inStream = new FileInputStream(file);
			this.streamReader = new InputStreamReader(this.inStream, charset);
			this.bufferedReader = new BufferedReader(this.streamReader);
//			this.bufferedReader = new LineReader(this.streamReader);
			this.currentReadingLine = 0;

			for (int i = 1; i < startRow; i++) {
				bufferedReader.readLine();
			}
		} catch (Exception e) {
			e.printStackTrace();
			throw new DataException(e);
		}
	}

	private void createWriter(boolean append) throws DataException {
		try {
			if (this.streamWriter != null) {
				this.streamWriter.close();
				this.outStream.close();
			}
			this.outStream = new FileOutputStream(file, append);
			this.streamWriter = new OutputStreamWriter(this.outStream, charset);
			this.currentWritenLine = 0;
		} catch (Exception e) {
			e.printStackTrace();
			throw new DataException(e);
		}
	}

	public void truncate() throws DataException {
		try {
			createWriter(false);
		} catch (DataException e) {
			e.printStackTrace();
			throw new DataException(e);
		}
	}

}
